<template>
  <div class="h-full w-full">
    <div ref="container"></div>
  </div>
</template>

<script setup>
import { Graph } from '@antv/x6'
import { CATEGORY_STATUS, DIRECTION, NODE_SIZE } from '@/constant'
import { ref, watch } from 'vue'
import { useAppStore } from '@/store'

const props = defineProps({
  itrainList: {
    type: Array,
    required: true,
  },
  currentCatId: {
    type: Number,
    default: -1,
  },
  isFinished: {
    type: Array,
    default: () => [],
  },
  checkedCatId: {
    type: Number,
    default: -1,
  },
  relativeCatId: {
    type: Number,
    default: -1,
  },
  hiddenNode: {
    type: Boolean,
    default: false,
  },
  draggable: {
    type: Boolean,
    default: false,
  },
})

const appStore = useAppStore()
const isDarkMode = ref(appStore.isDark)
const container = ref(null)

const graph = ref(null)

const createElements = (itrainList) => {
  let lastRow = null // 上一行的第一个节点 id
  let lastNode = null // 当前行上一个节点
  let realX = 0 // 节点实际渲染的 x 坐标
  let realY = -1 // 节点实际渲染的 y 坐标
  let prevY = 0 // 上一个节点的 y 坐标，用于判断是否应换行
  const cells = []

  // 添加节点表示关卡
  const addElement = (
    id,
    label,
    x, // 表示 x 轴位置，对应原数据的 `colY`
    y, // 表示 y 轴位置，对应原数据的 `rowX`
    secCatId,
    isFinished,
    status,
    openingTime
  ) => {
    // 隐藏的关卡不显示
    if (props.hiddenNode && status === CATEGORY_STATUS.HIDDEN) {
      return
    }
    const isHidden =
      status === CATEGORY_STATUS.HIDDEN ||
      (status === CATEGORY_STATUS.SCHEDULE_OPEN && openingTime > Date.now())
    const isCurrentCatId = secCatId === props.currentCatId
    // 计算节点实际的位置
    if (prevY !== y) {
      realY++
      realX = 0
      prevY = y
    } else {
      realX++
    }
    cells.push(
      graph.value.createNode({
        id: `${secCatId}`,
        shape: 'flow-node',
        // 数据中的 `rowY` 表示列数，在画布中表示 realX 坐标
        x: realX * NODE_SIZE.WIDTH,
        // 数据中的 `rowX` 表示行数，在画布中表示 realY 坐标
        y: realY * NODE_SIZE.HEIGHT,
        label: `(${y}.${x})${label.replace('->', '\n->')}`,
        attrs: {
          body: {
            fill: isCurrentCatId ? '#00ff60' : isHidden ? '#e3e8ee' : '#fff',
          },
          label: {
            fill:
              isFinished !== undefined && !isCurrentCatId
                ? isFinished
                  ? '#9baeae'
                  : '#ffa500'
                : '#000',
          },
        },
        ports: [
          {
            id: `${secCatId}-${DIRECTION.LEFT}`,
            group: 'left',
          },
          {
            id: `${secCatId}-${DIRECTION.RIGHT}`,
            group: 'right',
          },
          {
            id: `${secCatId}-${DIRECTION.TOP}`,
            group: 'top',
          },
          {
            id: `${secCatId}-${DIRECTION.BOTTOM}`,
            group: 'bottom',
          },
        ],
      })
    )

    let prev
    // 每行的第一个节点，需要构建从上一行到这一行的边
    if (realX === 0) {
      prev = lastRow
      lastRow = secCatId
      lastNode = secCatId
      if (prev === null) {
        return
      }
    } else {
      prev = lastNode
      lastNode = secCatId
    }
    // 添加边表示关卡之间的顺序关系
    cells.push(
      graph.value.createEdge({
        id: `e${prev}-${secCatId}`,
        shape: 'edge',
        source: {
          cell: `${prev}`,
          port: realX === 0 ? DIRECTION.RIGHT : DIRECTION.LEFT,
        },
        target: {
          cell: `${secCatId}`,
          port: realX === 0 ? DIRECTION.LEFT : DIRECTION.RIGHT,
        },
        attrs: {
          line: {
            targetMarker: 'block',
            stroke: '#1d4876',
            strokeWidth: 2,
          },
        },
        zIndex: 0,
      })
    )
  }
  itrainList.forEach((problem, i) => {
    addElement(
      i,
      problem.name,
      problem.colY,
      problem.rowX,
      problem.secCatId,
      props.isFinished[problem.secCatId],
      problem.status,
      problem.openingTime
    )
  })
  graph.value.resetCells(cells)
}

onMounted(() => {
  graph.value = new Graph({
    container: container.value,
    autoResize: true,
    panning: props.draggable,
    mousewheel: {
      enabled: props.draggable,
      maxScale: 1.5,
      minScale: 0.5,
    },
    interacting: {
      nodeMovable: false, // 禁止拖拽节点
    },
  })
  graph.value.translate(5, 5)
  createElements(props.itrainList)
  if (props.currentCatId > 0) {
    const node = graph.value.getCellById(`${props.currentCatId}`)
    node?.attr('body/fill', '#00ff60')
  }
  if (props.checkedCatId > 0) {
    const node = graph.value.getCellById(`${props.checkedCatId}`)
    node?.attr('body/stroke', isDarkMode.value ? '#2a456d' : '#1a365d')
    node?.attr('body/strokeWidth', 3)
  }
})

watch(
  () => props.itrainList,
  (newVal) => {
    if (container.value && props.itrainList?.length > 0) {
      createElements(newVal)
    }
  },
  { deep: true }
)

watch(
  () => props.relativeCatId,
  (newVal, oldVal) => {
    const newNode = graph.value.getCellById(`${newVal}`)
    const oldNode = graph.value.getCellById(`${oldVal}`)
    newNode?.attr('body/stroke', '#00ff60')
    newNode?.attr('body/strokeWidth', 3)
    oldNode?.attr('body/stroke', '#1a365d')
    oldNode?.attr('body/strokeWidth', 1)
  }
)

watch(
  () => props.checkedCatId,
  (newVal, oldVal) => {
    const newNode = graph.value.getCellById(`${newVal}`)
    const oldNode = graph.value.getCellById(`${oldVal}`)
    newNode?.attr('body/stroke', '#1a365d')
    newNode?.attr('body/strokeWidth', 3)
    oldNode?.attr('body/stroke', '#1a365d')
    oldNode?.attr('body/strokeWidth', 1)
  }
)
</script>
